home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / less-252 / screen.c < prev    next >
C/C++ Source or Header  |  1995-05-11  |  20KB  |  1,057 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines which deal with the characteristics of the terminal.
  30.  * Uses termcap to be as terminal-independent as possible.
  31.  *
  32.  * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
  33.  */
  34.  
  35. #include "less.h"
  36. #include "cmd.h"
  37.  
  38. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  39. #include <termios.h>
  40. #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
  41. #include <sys/ioctl.h>
  42. #endif
  43. #else
  44. #if HAVE_TERMIO_H
  45. #include <termio.h>
  46. #else
  47. #include <sgtty.h>
  48. #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
  49. #include <sys/ioctl.h>
  50. #endif
  51. #endif
  52. #endif
  53. #if HAVE_TERMCAP_H
  54. #include <termcap.h>
  55. #endif
  56.  
  57. #ifndef TIOCGWINSZ
  58. /*
  59.  * For the Unix PC (ATT 7300 & 3B1):
  60.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  61.  * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
  62.  */
  63. #include <sys/signal.h>
  64. #ifdef SIGPHONE
  65. #include <sys/window.h>
  66. #endif
  67. #endif
  68.  
  69. #if HAVE_SYS_STREAM_H
  70. #include <sys/stream.h>
  71. #endif
  72. #if HAVE_SYS_PTEM_H
  73. #include <sys/ptem.h>
  74. #endif
  75.  
  76.  
  77. /*
  78.  * Strings passed to tputs() to do various terminal functions.
  79.  */
  80. static char
  81.     *sc_pad,        /* Pad string */
  82.     *sc_home,        /* Cursor home */
  83.     *sc_addline,        /* Add line, scroll down following lines */
  84.     *sc_lower_left,        /* Cursor to last line, first column */
  85.     *sc_move,        /* General cursor positioning */
  86.     *sc_clear,        /* Clear screen */
  87.     *sc_eol_clear,        /* Clear to end of line */
  88.     *sc_eos_clear,        /* Clear to end of screen */
  89.     *sc_s_in,        /* Enter standout (highlighted) mode */
  90.     *sc_s_out,        /* Exit standout mode */
  91.     *sc_u_in,        /* Enter underline mode */
  92.     *sc_u_out,        /* Exit underline mode */
  93.     *sc_b_in,        /* Enter bold mode */
  94.     *sc_b_out,        /* Exit bold mode */
  95.     *sc_bl_in,        /* Enter blink mode */
  96.     *sc_bl_out,        /* Exit blink mode */
  97.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  98.     *sc_backspace,        /* Backspace cursor */
  99.     *sc_s_keypad,        /* Start keypad mode */
  100.     *sc_e_keypad,        /* End keypad mode */
  101.     *sc_init,        /* Startup terminal initialization */
  102.     *sc_deinit;        /* Exit terminal de-initialization */
  103.  
  104. static int init_done = 0;
  105.  
  106. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  107. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  108. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  109. public int sc_width, sc_height;    /* Height & width of screen */
  110. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  111. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  112. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  113. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  114. public int above_mem, below_mem;    /* Memory retained above/below screen */
  115.  
  116. static char *cheaper();
  117.  
  118. /*
  119.  * These two variables are sometimes defined in,
  120.  * and needed by, the termcap library.
  121.  */
  122. #if MUST_DEFINE_OSPEED
  123. extern short ospeed;    /* Terminal output baud rate */
  124. extern char PC;        /* Pad character */
  125. #endif
  126.  
  127. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  128. extern int know_dumb;        /* Don't complain about a dumb terminal */
  129. extern int back_scroll;
  130. extern int swindow;
  131. extern int no_init;
  132. extern char *tgetstr();
  133. extern char *tgoto();
  134.  
  135.  
  136. /*
  137.  * Change terminal to "raw mode", or restore to "normal" mode.
  138.  * "Raw mode" means 
  139.  *    1. An outstanding read will complete on receipt of a single keystroke.
  140.  *    2. Input is not echoed.  
  141.  *    3. On output, \n is mapped to \r\n.
  142.  *    4. \t is NOT expanded into spaces.
  143.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  144.  *       etc. are NOT disabled.
  145.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  146.  */
  147.     public void
  148. raw_mode(on)
  149.     int on;
  150. {
  151.     static int curr_on = 0;
  152.  
  153.     if (on == curr_on)
  154.         return;
  155. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  156.     {
  157.     struct termios s;
  158.     static struct termios save_term;
  159.  
  160.     if (on) 
  161.     {
  162.         /*
  163.          * Get terminal modes.
  164.          */
  165.         tcgetattr(2, &s);
  166.  
  167.         /*
  168.          * Save modes and set certain variables dependent on modes.
  169.          */
  170.         save_term = s;
  171. #if HAVE_OSPEED
  172.         switch (cfgetospeed(&s))
  173.         {
  174. #ifdef B0
  175.         case B0: ospeed = 0; break;
  176. #endif
  177. #ifdef B50
  178.         case B50: ospeed = 1; break;
  179. #endif
  180. #ifdef B75
  181.         case B75: ospeed = 2; break;
  182. #endif
  183. #ifdef B110
  184.         case B110: ospeed = 3; break;
  185. #endif
  186. #ifdef B134
  187.         case B134: ospeed = 4; break;
  188. #endif
  189. #ifdef B150
  190.         case B150: ospeed = 5; break;
  191. #endif
  192. #ifdef B200
  193.         case B200: ospeed = 6; break;
  194. #endif
  195. #ifdef B300
  196.         case B300: ospeed = 7; break;
  197. #endif
  198. #ifdef B600
  199.         case B600: ospeed = 8; break;
  200. #endif
  201. #ifdef B1200
  202.         case B1200: ospeed = 9; break;
  203. #endif
  204. #ifdef B1800
  205.         case B1800: ospeed = 10; break;
  206. #endif
  207. #ifdef B2400
  208.         case B2400: ospeed = 11; break;
  209. #endif
  210. #ifdef B4800
  211.         case B4800: ospeed = 12; break;
  212. #endif
  213. #ifdef B9600
  214.         case B9600: ospeed = 13; break;
  215. #endif
  216. #ifdef EXTA
  217.         case EXTA: ospeed = 14; break;
  218. #endif
  219. #ifdef EXTB
  220.         case EXTB: ospeed = 15; break;
  221. #endif
  222. #ifdef B57600
  223.         case B57600: ospeed = 16; break;
  224. #endif
  225. #ifdef B115200
  226.         case B115200: ospeed = 17; break;
  227. #endif
  228.         default: ;
  229.         }
  230. #endif
  231.         erase_char = s.c_cc[VERASE];
  232.         kill_char = s.c_cc[VKILL];
  233.  
  234.         /*
  235.          * Set the modes to the way we want them.
  236.          */
  237.         s.c_lflag &= ~(0
  238. #ifdef ICANON
  239.             | ICANON
  240. #endif
  241. #ifdef ECHO
  242.             | ECHO
  243. #endif
  244. #ifdef ECHOE
  245.             | ECHOE
  246. #endif
  247. #ifdef ECHOK
  248.             | ECHOK
  249. #endif
  250. #if ECHONL
  251.             | ECHONL
  252. #endif
  253.         );
  254.  
  255.         s.c_oflag |= (0
  256. #ifdef XTABS
  257.             | XTABS
  258. #else
  259. #ifdef TAB3
  260.             | TAB3
  261. #else
  262. #ifdef OXTABS
  263.             | OXTABS
  264. #endif
  265. #endif
  266. #endif
  267. #ifdef OPOST
  268.             | OPOST
  269. #endif
  270. #ifdef ONLCR
  271.             | ONLCR
  272. #endif
  273.         );
  274.  
  275.         s.c_oflag &= ~(0
  276. #ifdef ONOEOT
  277.             | ONOEOT
  278. #endif
  279. #ifdef OCRNL
  280.             | OCRNL
  281. #endif
  282. #ifdef ONOCR
  283.             | ONOCR
  284. #endif
  285. #ifdef ONLRET
  286.             | ONLRET
  287. #endif
  288.         );
  289.         s.c_cc[VMIN] = 1;
  290.         s.c_cc[VTIME] = 0;
  291.     } else
  292.     {
  293.         /*
  294.          * Restore saved modes.
  295.          */
  296.         s = save_term;
  297.     }
  298.     tcsetattr(2, TCSADRAIN, &s);
  299.     }
  300. #else
  301. #ifdef TCGETA
  302.     {
  303.     struct termio s;
  304.     static struct termio save_term;
  305.  
  306.     if (on)
  307.     {
  308.         /*
  309.          * Get terminal modes.
  310.          */
  311.         ioctl(2, TCGETA, &s);
  312.  
  313.         /*
  314.          * Save modes and set certain variables dependent on modes.
  315.          */
  316.         save_term = s;
  317. #if HAVE_OSPEED
  318.         ospeed = s.c_cflag & CBAUD;
  319. #endif
  320.         erase_char = s.c_cc[VERASE];
  321.         kill_char = s.c_cc[VKILL];
  322.  
  323.         /*
  324.          * Set the modes to the way we want them.
  325.          */
  326.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  327.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  328.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  329.         s.c_cc[VMIN] = 1;
  330.         s.c_cc[VTIME] = 0;
  331.     } else
  332.     {
  333.         /*
  334.          * Restore saved modes.
  335.          */
  336.         s = save_term;
  337.     }
  338.     ioctl(2, TCSETAW, &s);
  339.     }
  340. #else
  341.     {
  342.     struct sgttyb s;
  343.     static struct sgttyb save_term;
  344.  
  345.     if (on)
  346.     {
  347.         /*
  348.          * Get terminal modes.
  349.          */
  350.         ioctl(2, TIOCGETP, &s);
  351.  
  352.         /*
  353.          * Save modes and set certain variables dependent on modes.
  354.          */
  355.         save_term = s;
  356. #if HAVE_OSPEED
  357.         ospeed = s.sg_ospeed;
  358. #endif
  359.         erase_char = s.sg_erase;
  360.         kill_char = s.sg_kill;
  361.  
  362.         /*
  363.          * Set the modes to the way we want them.
  364.          */
  365.         s.sg_flags |= CBREAK;
  366.         s.sg_flags &= ~(ECHO|XTABS);
  367.     } else
  368.     {
  369.         /*
  370.          * Restore saved modes.
  371.          */
  372.         s = save_term;
  373.     }
  374.     ioctl(2, TIOCSETN, &s);
  375.     }
  376. #endif
  377. #endif
  378.     curr_on = on;
  379. }
  380.  
  381.     static void
  382. cannot(s)
  383.     char *s;
  384. {
  385.     PARG parg;
  386.  
  387.     if (know_dumb)
  388.         /* 
  389.          * User knows this is a dumb terminal, so don't tell him.
  390.          */
  391.         return;
  392.  
  393.     parg.p_string = s;
  394.     error("WARNING: terminal cannot %s", &parg);
  395. }
  396.  
  397. /*
  398.  * Get size of the output screen.
  399.  */
  400.     public void
  401. scrsize()
  402. {
  403.     register char *s;
  404. #ifdef TIOCGWINSZ
  405.     struct winsize w;
  406. #else
  407. #ifdef WIOCGETD
  408.     struct uwdata w;
  409. #endif
  410. #endif
  411.  
  412. #ifdef TIOCGWINSZ
  413.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  414.         sc_height = w.ws_row;
  415.     else
  416. #else
  417. #ifdef WIOCGETD
  418.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  419.         sc_height = w.uw_height/w.uw_vs;
  420.     else
  421. #endif
  422. #endif
  423.     if ((s = getenv("LINES")) != NULL)
  424.         sc_height = atoi(s);
  425.     else
  426.          sc_height = tgetnum("li");
  427.  
  428.     if (sc_height <= 0)
  429.         sc_height = 24;
  430.  
  431. #ifdef TIOCGWINSZ
  432.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  433.         sc_width = w.ws_col;
  434.     else
  435. #ifdef WIOCGETD
  436.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  437.         sc_width = w.uw_width/w.uw_hs;
  438.     else
  439. #endif
  440. #endif
  441.     if ((s = getenv("COLUMNS")) != NULL)
  442.         sc_width = atoi(s);
  443.     else
  444.          sc_width = tgetnum("co");
  445.  
  446.      if (sc_width <= 0)
  447.           sc_width = 80;
  448. }
  449.  
  450. /*
  451.  * Take care of the "variable" keys.
  452.  * Certain keys send escape sequences which differ on different terminals
  453.  * (such as the arrow keys, INSERT, DELETE, etc.)
  454.  * Construct the commands based on these keys.
  455.  */
  456.     public void
  457. get_editkeys()
  458. {
  459.     char *sp;
  460.     char *s;
  461.     char tbuf[40];
  462.  
  463.     static char kfcmdtable[400];
  464.     int sz_kfcmdtable = 0;
  465.     static char kecmdtable[400];
  466.     int sz_kecmdtable = 0;
  467.  
  468. #define    put_cmd(str,action,tbl,sz) { \
  469.     strcpy(tbl+sz, str);    \
  470.     sz += strlen(str) + 1;    \
  471.     tbl[sz++] = action; }
  472. #define    put_esc_cmd(str,action,tbl,sz) { \
  473.     tbl[sz++] = ESC; \
  474.     put_cmd(str,action,tbl,sz); }
  475.  
  476. #define    put_fcmd(str,action)    put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  477. #define    put_ecmd(str,action)    put_cmd(str,action,kecmdtable,sz_kecmdtable)
  478. #define    put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  479. #define    put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
  480.  
  481.     /*
  482.      * Look at some interesting keys and see what strings they send.
  483.      * Create commands (both command keys and line-edit keys).
  484.      */
  485.  
  486.     /* RIGHT ARROW */
  487.     sp = tbuf;
  488.     if ((s = tgetstr("kr", &sp)) != NULL)
  489.     {
  490.         put_ecmd(s, EC_RIGHT);
  491.         put_esc_ecmd(s, EC_W_RIGHT);
  492.     }
  493.     
  494.     /* LEFT ARROW */
  495.     sp = tbuf;
  496.     if ((s = tgetstr("kl", &sp)) != NULL)
  497.     {
  498.         put_ecmd(s, EC_LEFT);
  499.         put_esc_ecmd(s, EC_W_LEFT);
  500.     }
  501.     
  502.     /* UP ARROW */
  503.     sp = tbuf;
  504.     if ((s = tgetstr("ku", &sp)) != NULL) 
  505.     {
  506.         put_ecmd(s, EC_UP);
  507.         put_fcmd(s, A_B_LINE);
  508.     }
  509.         
  510.     /* DOWN ARROW */
  511.     sp = tbuf;
  512.     if ((s = tgetstr("kd", &sp)) != NULL) 
  513.     {
  514.         put_ecmd(s, EC_DOWN);
  515.         put_fcmd(s, A_F_LINE);
  516.     }
  517.  
  518.     /* PAGE UP */
  519.     sp = tbuf;
  520.     if ((s = tgetstr("kP", &sp)) != NULL) 
  521.     {
  522.         put_fcmd(s, A_B_SCREEN);
  523.     }
  524.  
  525.     /* PAGE DOWN */
  526.     sp = tbuf;
  527.     if ((s = tgetstr("kN", &sp)) != NULL) 
  528.     {
  529.         put_fcmd(s, A_F_SCREEN);
  530.     }
  531.     
  532.     /* HOME */
  533.     sp = tbuf;
  534.     if ((s = tgetstr("kh", &sp)) != NULL) 
  535.     {
  536.         put_ecmd(s, EC_HOME);
  537.     }
  538.  
  539.     /* END */
  540.     sp = tbuf;
  541.     if ((s = tgetstr("@7", &sp)) != NULL) 
  542.     {
  543.         put_ecmd(s, EC_END);
  544.     }
  545.  
  546.     /* DELETE */
  547.     sp = tbuf;
  548.     if ((s = tgetstr("kD", &sp)) == NULL) 
  549.     {
  550.         /* Use DEL (\177) if no "kD" termcap. */
  551.         tbuf[1] = '\177';
  552.         tbuf[2] = '\0';
  553.         s = tbuf+1;
  554.     }
  555.     put_ecmd(s, EC_DELETE);
  556.     put_esc_ecmd(s, EC_W_DELETE);
  557.         
  558.     /* BACKSPACE */
  559.     tbuf[0] = ESC;
  560.     tbuf[1] = erase_char;
  561.     tbuf[2] = '\0';
  562.     put_ecmd(tbuf, EC_W_BACKSPACE);
  563.  
  564.     /*
  565.      * Register the two tables.
  566.      */
  567.     add_fcmd_table(kfcmdtable, sz_kfcmdtable);
  568.     add_ecmd_table(kecmdtable, sz_kecmdtable);
  569. }
  570.  
  571. /*
  572.  * Get terminal capabilities via termcap.
  573.  */
  574.     public void
  575. get_term()
  576. {
  577.     char *sp;
  578.     register char *t1, *t2;
  579.     register int hard;
  580.     char *term;
  581.     char termbuf[2048];
  582.  
  583.     static char sbuf[1024];
  584.  
  585.     /*
  586.      * Find out what kind of terminal this is.
  587.      */
  588.      if ((term = getenv("TERM")) == NULL)
  589.          term = "unknown";
  590.      if (tgetent(termbuf, term) <= 0)
  591.          strcpy(termbuf, "dumb:hc:");
  592.  
  593.      hard = tgetflag("hc");
  594.  
  595.     /*
  596.      * Get size of the screen.
  597.      */
  598.     scrsize();
  599.     pos_init();
  600.  
  601.     auto_wrap = tgetflag("am");
  602.     ignaw = tgetflag("xn");
  603.     above_mem = tgetflag("da");
  604.     below_mem = tgetflag("db");
  605.  
  606.     /*
  607.      * Assumes termcap variable "sg" is the printing width of:
  608.      * the standout sequence, the end standout sequence,
  609.      * the underline sequence, the end underline sequence,
  610.      * the boldface sequence, and the end boldface sequence.
  611.      */
  612.     if ((so_s_width = tgetnum("sg")) < 0)
  613.         so_s_width = 0;
  614.     so_e_width = so_s_width;
  615.  
  616.     bo_s_width = bo_e_width = so_s_width;
  617.     ul_s_width = ul_e_width = so_s_width;
  618.     bl_s_width = bl_e_width = so_s_width;
  619.  
  620.     /*
  621.      * Get various string-valued capabilities.
  622.      */
  623.     sp = sbuf;
  624.  
  625. #if HAVE_OSPEED
  626.     sc_pad = tgetstr("pc", &sp);
  627.     if (sc_pad != NULL)
  628.         PC = *sc_pad;
  629. #endif
  630.  
  631.     sc_s_keypad = tgetstr("ks", &sp);
  632.     if (sc_s_keypad == NULL)
  633.         sc_s_keypad = "";
  634.     sc_e_keypad = tgetstr("ke", &sp);
  635.     if (sc_e_keypad == NULL)
  636.         sc_e_keypad = "";
  637.         
  638.     sc_init = tgetstr("ti", &sp);
  639.     if (sc_init == NULL)
  640.         sc_init = "";
  641.  
  642.     sc_deinit= tgetstr("te", &sp);
  643.     if (sc_deinit == NULL)
  644.         sc_deinit = "";
  645.  
  646.     sc_eol_clear = tgetstr("ce", &sp);
  647.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  648.     {
  649.         cannot("clear to end of line");
  650.         sc_eol_clear = "";
  651.     }
  652.  
  653.     sc_eos_clear = tgetstr("cd", &sp);
  654.     if (below_mem && 
  655.         (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  656.     {
  657.         cannot("clear to end of screen");
  658.         sc_eol_clear = "";
  659.     }
  660.  
  661.     sc_clear = tgetstr("cl", &sp);
  662.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  663.     {
  664.         cannot("clear screen");
  665.         sc_clear = "\n\n";
  666.     }
  667.  
  668.     sc_move = tgetstr("cm", &sp);
  669.     if (hard || sc_move == NULL || *sc_move == '\0')
  670.     {
  671.         /*
  672.          * This is not an error here, because we don't 
  673.          * always need sc_move.
  674.          * We need it only if we don't have home or lower-left.
  675.          */
  676.         sc_move = "";
  677.     }
  678.  
  679.     sc_s_in = tgetstr("so", &sp);
  680.     if (hard || sc_s_in == NULL)
  681.         sc_s_in = "";
  682.  
  683.     sc_s_out = tgetstr("se", &sp);
  684.     if (hard || sc_s_out == NULL)
  685.         sc_s_out = "";
  686.  
  687.     sc_u_in = tgetstr("us", &sp);
  688.     if (hard || sc_u_in == NULL)
  689.         sc_u_in = sc_s_in;
  690.  
  691.     sc_u_out = tgetstr("ue", &sp);
  692.     if (hard || sc_u_out == NULL)
  693.         sc_u_out = sc_s_out;
  694.  
  695.     sc_b_in = tgetstr("md", &sp);
  696.     if (hard || sc_b_in == NULL)
  697.     {
  698.         sc_b_in = sc_s_in;
  699.         sc_b_out = sc_s_out;
  700.     } else
  701.     {
  702.         sc_b_out = tgetstr("me", &sp);
  703.         if (hard || sc_b_out == NULL)
  704.             sc_b_out = "";
  705.     }
  706.  
  707.     sc_bl_in = tgetstr("mb", &sp);
  708.     if (hard || sc_bl_in == NULL)
  709.     {
  710.         sc_bl_in = sc_s_in;
  711.         sc_bl_out = sc_s_out;
  712.     } else
  713.     {
  714.         sc_bl_out = tgetstr("me", &sp);
  715.         if (hard || sc_bl_out == NULL)
  716.             sc_bl_out = "";
  717.     }
  718.  
  719.     sc_visual_bell = tgetstr("vb", &sp);
  720.     if (hard || sc_visual_bell == NULL)
  721.         sc_visual_bell = "";
  722.  
  723.     if (tgetflag("bs"))
  724.         sc_backspace = "\b";
  725.     else
  726.     {
  727.         sc_backspace = tgetstr("bc", &sp);
  728.         if (sc_backspace == NULL || *sc_backspace == '\0')
  729.             sc_backspace = "\b";
  730.     }
  731.  
  732.     /*
  733.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  734.      * to move the cursor to the upper left corner of the screen.
  735.      */
  736.     t1 = tgetstr("ho", &sp);
  737.     if (hard || t1 == NULL)
  738.         t1 = "";
  739.     if (*sc_move == '\0')
  740.         t2 = "";
  741.     else
  742.     {
  743.         strcpy(sp, tgoto(sc_move, 0, 0));
  744.         t2 = sp;
  745.         sp += strlen(sp) + 1;
  746.     }
  747.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  748.  
  749.     /*
  750.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  751.      * to move the cursor to the lower left corner of the screen.
  752.      */
  753.     t1 = tgetstr("ll", &sp);
  754.     if (hard || t1 == NULL)
  755.         t1 = "";
  756.     if (*sc_move == '\0')
  757.         t2 = "";
  758.     else
  759.     {
  760.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  761.         t2 = sp;
  762.         sp += strlen(sp) + 1;
  763.     }
  764.     sc_lower_left = cheaper(t1, t2,
  765.         "move cursor to lower left of screen", "\r");
  766.  
  767.     /*
  768.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  769.      * to add a line at the top of the screen.
  770.      */
  771.     t1 = tgetstr("al", &sp);
  772.     if (hard || t1 == NULL)
  773.         t1 = "";
  774.     t2 = tgetstr("sr", &sp);
  775.     if (hard || t2 == NULL)
  776.         t2 = "";
  777.     if (above_mem)
  778.         sc_addline = t1;
  779.     else
  780.         sc_addline = cheaper(t1, t2, "scroll backwards", "");
  781.     if (*sc_addline == '\0')
  782.     {
  783.         /*
  784.          * Force repaint on any backward movement.
  785.          */
  786.         back_scroll = 0;
  787.     }
  788. }
  789.  
  790. /*
  791.  * Return the cost of displaying a termcap string.
  792.  * We use the trick of calling tputs, but as a char printing function
  793.  * we give it inc_costcount, which just increments "costcount".
  794.  * This tells us how many chars would be printed by using this string.
  795.  * {{ Couldn't we just use strlen? }}
  796.  */
  797. static int costcount;
  798.  
  799. /*ARGSUSED*/
  800.     static int
  801. inc_costcount(c)
  802.     int c;
  803. {
  804.     costcount++;
  805.     return (c);
  806. }
  807.  
  808.     static int
  809. cost(t)
  810.     char *t;
  811. {
  812.     costcount = 0;
  813.     tputs(t, sc_height, inc_costcount);
  814.     return (costcount);
  815. }
  816.  
  817. /*
  818.  * Return the "best" of the two given termcap strings.
  819.  * The best, if both exist, is the one with the lower 
  820.  * cost (see cost() function).
  821.  */
  822.     static char *
  823. cheaper(t1, t2, doit, def)
  824.     char *t1, *t2;
  825.     char *doit;
  826.     char *def;
  827. {
  828.     if (*t1 == '\0' && *t2 == '\0')
  829.     {
  830.         cannot(doit);
  831.         return (def);
  832.     }
  833.     if (*t1 == '\0')
  834.         return (t2);
  835.     if (*t2 == '\0')
  836.         return (t1);
  837.     if (cost(t1) < cost(t2))
  838.         return (t1);
  839.     return (t2);
  840. }
  841.  
  842.  
  843. /*
  844.  * Below are the functions which perform all the 
  845.  * terminal-specific screen manipulation.
  846.  */
  847.  
  848.  
  849. /*
  850.  * Initialize terminal
  851.  */
  852.     public void
  853. init()
  854. {
  855.     if (no_init)
  856.         return;
  857.     tputs(sc_init, sc_height, putchr);
  858.     tputs(sc_s_keypad, sc_height, putchr);
  859.     init_done = 1;
  860. }
  861.  
  862. /*
  863.  * Deinitialize terminal
  864.  */
  865.     public void
  866. deinit()
  867. {
  868.     if (no_init)
  869.         return;
  870.     if (!init_done)
  871.         return;
  872.     tputs(sc_e_keypad, sc_height, putchr);
  873.     tputs(sc_deinit, sc_height, putchr);
  874.     init_done = 0;
  875. }
  876.  
  877. /*
  878.  * Home cursor (move to upper left corner of screen).
  879.  */
  880.     public void
  881. home()
  882. {
  883.     tputs(sc_home, 1, putchr);
  884. }
  885.  
  886. /*
  887.  * Add a blank line (called with cursor at home).
  888.  * Should scroll the display down.
  889.  */
  890.     public void
  891. add_line()
  892. {
  893.     tputs(sc_addline, sc_height, putchr);
  894. }
  895.  
  896. /*
  897.  * Move cursor to lower left corner of screen.
  898.  */
  899.     public void
  900. lower_left()
  901. {
  902.     tputs(sc_lower_left, 1, putchr);
  903. }
  904.  
  905. /*
  906.  * Ring the terminal bell.
  907.  */
  908.     public void
  909. bell()
  910. {
  911.     if (quiet == VERY_QUIET)
  912.         vbell();
  913.     else
  914.         putchr('\7');
  915. }
  916.  
  917. /*
  918.  * Output the "visual bell", if there is one.
  919.  */
  920.     public void
  921. vbell()
  922. {
  923.     if (*sc_visual_bell == '\0')
  924.         return;
  925.     tputs(sc_visual_bell, sc_height, putchr);
  926. }
  927.  
  928. /*
  929.  * Clear the screen.
  930.  */
  931.     public void
  932. clear()
  933. {
  934.     tputs(sc_clear, sc_height, putchr);
  935. }
  936.  
  937. /*
  938.  * Clear from the cursor to the end of the cursor's line.
  939.  * {{ This must not move the cursor. }}
  940.  */
  941.     public void
  942. clear_eol()
  943. {
  944.     tputs(sc_eol_clear, 1, putchr);
  945. }
  946.  
  947. /*
  948.  * Clear the bottom line of the display.
  949.  * Leave the cursor at the beginning of the bottom line.
  950.  */
  951.     public void
  952. clear_bot()
  953. {
  954.     lower_left();
  955.     if (below_mem)
  956.         tputs(sc_eos_clear, 1, putchr);
  957.     else
  958.         tputs(sc_eol_clear, 1, putchr);
  959. }
  960.  
  961. /*
  962.  * Begin "standout" (bold, underline, or whatever).
  963.  */
  964.     public void
  965. so_enter()
  966. {
  967.     tputs(sc_s_in, 1, putchr);
  968. }
  969.  
  970. /*
  971.  * End "standout".
  972.  */
  973.     public void
  974. so_exit()
  975. {
  976.     tputs(sc_s_out, 1, putchr);
  977. }
  978.  
  979. /*
  980.  * Begin "underline" (hopefully real underlining, 
  981.  * otherwise whatever the terminal provides).
  982.  */
  983.     public void
  984. ul_enter()
  985. {
  986.     tputs(sc_u_in, 1, putchr);
  987. }
  988.  
  989. /*
  990.  * End "underline".
  991.  */
  992.     public void
  993. ul_exit()
  994. {
  995.     tputs(sc_u_out, 1, putchr);
  996. }
  997.  
  998. /*
  999.  * Begin "bold"
  1000.  */
  1001.     public void
  1002. bo_enter()
  1003. {
  1004.     tputs(sc_b_in, 1, putchr);
  1005. }
  1006.  
  1007. /*
  1008.  * End "bold".
  1009.  */
  1010.     public void
  1011. bo_exit()
  1012. {
  1013.     tputs(sc_b_out, 1, putchr);
  1014. }
  1015.  
  1016. /*
  1017.  * Begin "blink"
  1018.  */
  1019.     public void
  1020. bl_enter()
  1021. {
  1022.     tputs(sc_bl_in, 1, putchr);
  1023. }
  1024.  
  1025. /*
  1026.  * End "blink".
  1027.  */
  1028.     public void
  1029. bl_exit()
  1030. {
  1031.     tputs(sc_bl_out, 1, putchr);
  1032. }
  1033.  
  1034. /*
  1035.  * Erase the character to the left of the cursor 
  1036.  * and move the cursor left.
  1037.  */
  1038.     public void
  1039. backspace()
  1040. {
  1041.     /* 
  1042.      * Try to erase the previous character by overstriking with a space.
  1043.      */
  1044.     tputs(sc_backspace, 1, putchr);
  1045.     putchr(' ');
  1046.     tputs(sc_backspace, 1, putchr);
  1047. }
  1048.  
  1049. /*
  1050.  * Output a plain backspace, without erasing the previous char.
  1051.  */
  1052.     public void
  1053. putbs()
  1054. {
  1055.     tputs(sc_backspace, 1, putchr);
  1056. }
  1057.